package com.xinxin.blockchain.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.xinxin.base.entity.PageRequest;
import com.xinxin.base.enums.TokenTypeEnum;
import com.xinxin.base.exception.BusinessException;
import com.xinxin.base.service.IRedisService;
import com.xinxin.base.service.ITokenService;
import com.xinxin.base.util.*;
import com.xinxin.blockchain.constant.ErrorCode;
import com.xinxin.blockchain.constant.RedisKeyConstant;
import com.xinxin.blockchain.dao.impl.MemberDao;
import com.xinxin.blockchain.enums.MsgCodeTypeEnum;
import com.xinxin.blockchain.enums.SignUpTypeEnum;
import com.xinxin.blockchain.model.Member;
import com.xinxin.blockchain.service.IMemberService;
import com.xinxin.blockchain.vo.MemberLoginVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author ys
 * @date 2021/5/19
 */
@Service
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class MemberServiceImpl extends MemberDao implements IMemberService {

    @Autowired
    private HttpServletRequest request;

    @Autowired
    private IRedisService redisService;
    @Autowired
    private ITokenService tokenService;

    private String getAccountSecret0(Long accountId) {
        // 从缓存获取账户密钥
        String key = RedisKeyConstant.ACCOUNT_SECRET + accountId;
        String secret = redisService.getString(key);
        if (secret != null) {
            return secret;
        }

        // 缓存没有则从数据库查询
        Member member = super.getById(accountId);
        if (member == null || !member.getDataStatus() || member.getStatus() != 1) {
            return null;
        }

        // 密钥:注册类型
        secret = member.getSecret() + ":" + member.getRegisterType();
        // 存入缓存
        redisService.setString(key, secret);

        return secret;
    }

    @Override
    public String getAccountSecret(Long accountId) {
        String secret = getAccountSecret0(accountId);
        if (secret == null) {
            return null;
        }

        String[] parts = secret.split(":");
        // 检查注册类型，不同项目不同实现
        if (!checkSignUpType(parts[1])) {
            return null;
        }

        return parts[0];
    }

    protected boolean checkSignUpType(String type) {
        SignUpTypeEnum signUpType = SignUpTypeEnum.getByType(type);
        return signUpType != null && signUpType != SignUpTypeEnum.ROBOT;
    }

    @Override
    public Member getMemberByMobile(String fullMobile) {
        Member member = super.getMemberByMobile(fullMobile);
        if (member != null && member.getStatus() != 1) {
            throw new BusinessException(ErrorCode.ACCOUNT_IS_DISABLED);
        }

        return member;
    }

    @Override
    @Transactional
    public MemberLoginVO doLogin(Member member) {
        Long accountId = member.getId();
        String secret = CommonUtil.generateUUID();

        Member update = new Member()
                .setId(accountId)
                .setSecret(secret)
                .setLastLoginIp(IPUtil.getIpAddr(request))
                .setLastLoginTime(LocalDateTime.now());
        super.updateById(update);

        redisService.delete(RedisKeyConstant.ACCOUNT_SECRET + accountId);

        MemberLoginVO memberLoginVO = new MemberLoginVO();
        memberLoginVO.setId(accountId);
        memberLoginVO.setAccount(member.getAccount());
        memberLoginVO.setToken(tokenService.generateToken(TokenTypeEnum.AUTHORIZATION, accountId, secret));
        memberLoginVO.setRefreshToken(tokenService.generateToken(TokenTypeEnum.REFRESH, accountId, secret));

        return memberLoginVO;
    }

    @Override
    @Transactional
    public MemberLoginVO mobileLogin(Integer countryCode, String mobile, String smsCode) {
        String fullMobile = countryCode + "-" + mobile;
        checkCode(fullMobile, smsCode);

        Member member = getMemberByMobile(fullMobile);
        if (member == null) {
            member = mobileSignUp(countryCode, mobile);
        }

        return doLogin(member);
    }

    private void checkCode(String fullMobile, String smsCode) {
        String key = MsgCodeTypeEnum.SIGN_UP_IN.getRedisKey() + fullMobile;
        String code = redisService.getObject(key, String.class);
        if (!smsCode.equals(code)) {
            throw new BusinessException(ErrorCode.CAPTCHA_NOT_VALID);
        }
        redisService.delete(key);
    }

    private Member mobileSignUp(Integer countryCode, String mobile) {
        Member member = new Member()
                .setAccount(mobile)
                .setPassword("NONE")
                .setMobile(countryCode + "-" + mobile)
                .setRegisterIp(IPUtil.getIpAddr(request))
                .setRegisterType(SignUpTypeEnum.MOBILE.getType());
        super.save(member);
        return member;
    }

    @Override
    public PageInfo<Member> listMember(PageRequest request) {
        PageHelper.startPage(request.getPageNum(), request.getPageSize());
        List<Member> list = super.listMember(SignUpTypeEnum.ROBOT.getType(), "`create_time`");
        return new PageInfo<>(list);
    }

    @Override
    public Member searchMember(String account) {
        Member member = redisService.getObject("member:" + account, Member.class);
        if (member != null) {
            return member;
        }

        member = super.searchMember(account);
        if (member != null) {
            redisService.setObject("member:" + account, member, 15, TimeUnit.SECONDS);
        }

        return member;
    }

    @Override
    @Transactional
    public Long createMember(String account, String password, boolean rollback) {
        Member member = new Member()
                .setAccount(account)
                .setPassword(MD5Util.encrypt(account, password));

        String ip = IPUtil.getIpAddr(HttpContextUtil.getHttpServletRequest());
        String cityInfo = RegionUtil.getCityInfo(ip);
        member.setSecret(cityInfo);

        member.setRegisterIp(ip)
                .setRegisterType(SignUpTypeEnum.ROBOT.getType())
                .setLastLoginIp(ip);

        super.insertSelective(member);
        super.save(member);
        if (rollback) {
            throw new BusinessException(996, "测试回滚");
        }

        return member.getId();
    }
}
